package cn.edu.cug.cs.gtl.docsrv.img.manager;

import cn.edu.cug.cs.gtl.common.Pair;
import cn.edu.cug.cs.gtl.docsrv.img.block.ImageBlock;
import cn.edu.cug.cs.gtl.docsrv.img.catalog.CatalogTree;
import cn.edu.cug.cs.gtl.docsrv.img.querier.BlockQuerier;
import cn.edu.cug.cs.gtl.docsrv.img.updater.BlockUpdater;
import cn.edu.cug.cs.gtl.extractor.ImageExtractor;
import cn.edu.cug.cs.gtl.io.FileDataSplitter;
import cn.edu.cug.cs.gtl.mybatis.BatisWrapper;
import cn.edu.cug.cs.gtl.mybatis.mapper.img.*;


import cn.edu.cug.cs.gtl.mybatis.Session;

import cn.edu.cug.cs.gtl.protos.TimeValue;
import cn.edu.cug.cs.gtl.util.ZipUtils;
import org.locationtech.jts.geom.Envelope;

import java.awt.image.BufferedImage;
import java.io.File;
import java.math.BigDecimal;

import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public abstract class ImgManager {

    protected Session session;//开启数据库会话

    /**
     * 构造函数
     * @param session
     */
    public ImgManager(Session session){
        this.session = session;
    }

    /**
     * 设置数据库会话
     * @param s
     */
    public void setSession(Session s){
        this.session=s;
    }

    /**
     * 将一个由imgItem+imgFile组成的图像文档插入数据库
     * @param imgItem 图像元数据信息
     * @param imgFile 图像文件内容
     * @return
     */
    public abstract boolean insert(ImgItem imgItem, ImgFile imgFile);

    /**
     * 将一个由ImgAll组成的图像文档插入数据库
     * @param imgAll 图像文档
     * @return 成功返回true,否则返回false
     */
    public abstract boolean insert(ImgAll imgAll);

    /**
     * 读取本地目录中的所有图像文档，将其插入图像库中
     * @param localDir 本地目录
     * @return 成功返回true,否则返回false
     */
    public abstract boolean insert(String localDir);
//    /**
//     * 读取本地目录中的所有图像文档，将其插入图像库中，同时插入指定的文档库
//     * @param localDir 本地目录
//     * @param docManager 文档数据管理器
//     * @return
//     */
//    @Deprecated //不应该在这个层次进行跨库操作,移植到Managers类中。
//    public abstract boolean insert(String localDir, DocManager docManager);


    /**
     * 根据imgInternalId删除数据库中对应的图像文档记录
     * @param imgItem 必须填充imgInternalId
     * @return
     */
    public abstract boolean delete(ImgItem imgItem);

    /**
     * 根据imgInternalId删除数据库中对应的图像文档记录
     * @param imgAll 必须填充imgInternalId
     * @return
     */
    public abstract boolean delete(ImgAll imgAll);

    /**
     * 根据imgItem+imgFile更新数据库中的图像文档记录
     * @param imgItem 该对象的imgInternalId必须填充
     * @param imgFile 更新文档文件的内容
     * @return 更新成功返回true,否则返回false
     */
    public abstract boolean update(ImgItem imgItem, ImgFile imgFile);

    /**
     * 根据根据imgAll中的信息更新数据库中的记录
     * @param imgAll 该对象的imgInternalId必须填充
     * @return 更新成功返回true,否则返回false
     */
    public abstract boolean update(ImgAll imgAll);

//
//    /**
//     * 从文档库中提取入库时间段[begin,end)之内的图像文档
//     * @param docManager 指定的文档库
//     * @param begin 文档入库时间
//     * @param end  文档入库时间
//     * @return List<ImgAll> imgAll列表
//     */
//    @Deprecated //不应该在这个层次进行跨库操作,移植到Managers类中。
//    public  List<ImgAll> extractImgDocument(DocManager docManager, TimeValue begin, TimeValue end){
//        List<ImgAll> imgAllList = new ArrayList<>();
//        List<DocAll> docAllList = docManager.queryByAppendDate(begin, end);
//
//        for (DocAll docAll: docAllList){
//            ImgAll imgAll = new ImgAll();
//            ImgUtils.toImgAll(docAll);
//            imgAllList.add(imgAll);
//        }
//        return imgAllList;
//    }
//
//    /**
//     * 从文档库中提取指定名称和版本的图像文档
//     * @param docManager 指定的文档库
//     * @param title
//     * @param version
//     * @return ImgAll
//     */
//    @Deprecated //不应该在这个层次进行跨库操作,移植到Managers类中。
//    public ImgAll extractImgDocument(DocManager docManager, String title, String version){
//        List<DocAll> docAllList = docManager.queryByTitle(title);
//        for (DocAll docAll: docAllList){
//            if (docAll.getDocVersion().equals(version)){
//                ImgAll imgAll = new ImgAll();
//                ImgUtils.toImgAll(docAll);
//                return imgAll;
//            }
//        }
//        return null;
//    }

    /**
     * 根据imgInternalId查询文档
     * @param imgInternalId
     * @return ImgAll
     */
    public abstract ImgAll queryByInternalId(BigDecimal imgInternalId);

    /**
     * 根据文档标题查询文档
     * @param imgEnTitle
     * @return List<imgAll>
     */
    public abstract List<ImgAll> queryByEnTitle(String imgEnTitle);

    /**
     * 根据文档注释查询文档
     * @param imgCnTitle  文档注释
     * @return
     */
    public abstract List<ImgAll> queryByComment(String imgCnTitle);

    /**
     * 根据文档类型查询文档
     * @param imgType
     * @return
     */
    public abstract List<ImgAll> queryByType(String imgType);

    /**
     * 从文档库中提取文档添加时间段为[begin,end)之内的文档
     * @param begin
     * @param end
     * @return
     */
    public abstract List<ImgAll> queryByAppendDate(TimeValue begin, TimeValue end);

    /**
     * 查询得到所有ShapeBlock的边界矩形
     * @return
     */
    public abstract List<Pair<BigDecimal, Envelope>> queryAllEnvelopes();

    /**
     * 根据传入参数在图像库中进行查询
     * {
     *     imgInternalId:0
     *     imgEnTitle:"tomcat"
     *     imgCnTitle:"汤姆猫"
     * }
     * @param parameters
     *     Type(Object)  Name(String)
     *     BigDecimal imgInternalId;
     *
     *     String imgEnTitle;
     *
     *     String imgCnTitle;
     *
     *     String imgUuid;
     *
     *     String imgVersion;
     *
     *     String imgKeywords;
     *
     *     String imgAbstract;
     *
     *     String imgContent;
     *
     *     String imgType;
     *
     *     String imgAuthors;
     *
     *     String imgAffiliation;
     *
     *     String imgTags;
     *
     *     String imgUrl;
     *
     *     Date imgAppendDate;
     *
     *     Date imgUpdateDate;
     *
     *     Date imgCreateDate;
     *
     *     String imgMemo;
     *
     *     BigDecimal imgCatalog;
     *
     *     String imgExtent;
     *
     * @return 返回查询到的文档。
     */
    public  abstract List<ImgAll> query(Map<String,Object> parameters);

    /**
     * 获取数据库内所有的图块的名称
     * @return
     */
    public List<String> getAllBlockNames(){
        if(session.isClosed())
            return null;
        ImgItemMapper imgItemMapper = session.getMapper(ImgItemMapper.class);
        List<ImgItem> ls = imgItemMapper.selectAll();
        if(ls==null || ls.size()==0) return null;
        return ls.stream().map(i->i.getImgEnTitle()).collect(Collectors.toList());
    }

    /**
     *
     * @return
     */
    public List<ImgCatalog> getCatalogs(){
        if(session.isClosed())
            return null;
        ImgCatalogMapper imgMapper = session.getMapper(ImgCatalogMapper.class);
        List<ImgCatalog> ls = imgMapper.selectAll();
        if(ls==null || ls.size()==0) return null;
        return ls;
    }

    /**
     * 如果catalogName 为 中国/湖北/武汉,则返回的是一个字符串对象的数组，
     * 含有三个元素中国、湖北、武汉，表示三级分类
     * @param catalogInternalId
     * @return
     */
    public String[] getCatalogName(BigDecimal catalogInternalId){
        if(session.isClosed())
            return null;
        ImgCatalogMapper imgMapper = session.getMapper(ImgCatalogMapper.class);
        ImgCatalog ls = imgMapper.selectByPrimaryKey(catalogInternalId);
        String c = ls.getCatalogName();
        if(c==null || c.isEmpty()) return null;
        if(!c.contains("/")){
            String [] ss = new String[1];
            ss[0]=c;
            return ss;
        }
        else{
            return c.split("/");
        }
    }

    /**
     * CatalogOrder用于对分类名称进行排序，以便在前端可以按照这个设置进行显示排序
     * @return
     */
    public String getCatalogOrder(BigDecimal catalogInternalId){
        if(session.isClosed())
            return null;
        ImgCatalogMapper imgMapper = session.getMapper(ImgCatalogMapper.class);
        ImgCatalog ls = imgMapper.selectByPrimaryKey(catalogInternalId);
        return ls.getCatalogOrder();
    }

    /**
     * 根据指定的本地目录，
     * 获取其下的所有子目录，
     * 构建ImgCatalog
     * 例如：传入的参数是/Users/ZhenwenHe/git/data
     * 在该目录下的data目录结构如下：
     *                         spatialdata/wuhan
     *                         spatialdata/beijing
     *                         imagedata/wuhan
     *                         imagedata/beijing
     * 则会生成7个ImgCatalog,其name分别是：
     *                         data
     *                         data/spatialdata
     *                         data/imagedata
     *                         data/spatialdata/wuhan
     *                         data/spatialdata/beijing
     *                         data/imagedata/wuhan
     *                         data/imagedata/beijing
     * 然后会和IMG_CATALOG中的数据进行比较，
     * 如果存在，则修改其ID和ORDER
     * 如果不存在，则插入IMG_CATALOG中
     * @param localDir
     * @return 返回数据库中，当前目录下所有合格的分类对象
     */
    public List<ImgCatalog> localDirToCatalogs(String localDir ){
        try {
            cn.edu.cug.cs.gtl.io.File f = new cn.edu.cug.cs.gtl.io.File(localDir);
            if(!f.exists()) return null;

            if(session.isClosed())
                session= BatisWrapper.getInstance().getMasterSession();

            ImgCatalogMapper mapper = session.getMapper(ImgCatalogMapper.class);
            if(mapper==null) return null;

            List<ImgCatalog> ls= new ArrayList<>();
            dirToCatalogs(null,localDir, ls);

            //去除数据库中已经存在的分类，并赋值相应的ID
            BigDecimal id = mapper.maxInternalId();
            id = id.add(BigDecimal.ONE);
            List<ImgCatalog> existCatalogs = mapper.selectAll();
            boolean exist = false;
            for(ImgCatalog i: ls){
                for(ImgCatalog j : existCatalogs){
                    exist=false;
                    //如果存在，则直接拷贝已经存在的ImgCatalog
                    if(i.getCatalogName().equals(j.getCatalogName())){
                        i.setCatalogInternalId(j.getCatalogInternalId());
                        if(j.getCatalogOrder()!=null)
                            i.setCatalogOrder(j.getCatalogOrder());
                        exist=true;
                        break;
                    }
                }
                if(exist==false){//如果不存在，插入数据库
                    i.setCatalogInternalId(id);
                    id = id.add(BigDecimal.ONE);
                    mapper.insert(i);
                }
                else{//存在
                    continue;
                }
            }
            session.commit();
            return ls;
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 采用递归方法，只供List<ImgCatalog> dirToCatalogs(String localDir )调用
     * @param parentCatalogName
     * @param localDir
     * @param ls
     * @throws Exception
     */
    private   static void dirToCatalogs(
            String parentCatalogName,
            String localDir,
            List<ImgCatalog>ls) throws Exception{

        cn.edu.cug.cs.gtl.io.File f = new cn.edu.cug.cs.gtl.io.File(localDir);
        if(!f.exists()) return;

        if(f.isDirectory()){
            String [] ss = cn.edu.cug.cs.gtl.io.File.split(localDir);
            ImgCatalog imgCatalog = new ImgCatalog();
            //imgCatalog.setCatalogInternalId(imgCatalogMapper.maxInternalId().add(BigDecimal.ONE));
            if(parentCatalogName==null || parentCatalogName.isEmpty()) {
                parentCatalogName = ss[ss.length - 1];
                imgCatalog.setCatalogName(parentCatalogName);
            }
            else {
                parentCatalogName=parentCatalogName + File.separator + ss[ss.length - 1];
                imgCatalog.setCatalogName(parentCatalogName);
            }
            ls.add(imgCatalog);
            for(File fo: f.listFiles()){
                if(!fo.isDirectory())
                    continue;
                dirToCatalogs(parentCatalogName,fo.getAbsolutePath(),ls);
            }
            return;
        }
        else {
            return;
        }
    }
    /**
     * 获取图像块ImageBlock级别的查询器
     * @return
     */
    public abstract BlockQuerier getBlockQuerier();

    /**
     * 获取图像块ImageBlock级别的更新器
     * @return
     */
    public abstract BlockUpdater getBlockUpdater();
    /**
     * 将ImgAll转换成要素对象数组
     * @param imgAll 其中的ImgRawBytes，必须是JPG/PNG/TIF等文件的ZIP文件内容
     * @return
     * @throws Exception
     */
    public static List<BufferedImage> toImages(ImgAll imgAll) throws Exception{
        return ImageExtractor.parseToImages(imgAll.getImgRawBytes());
    }

    /**
     * 将ImgAll转换成图像单元块
     * @param imgAll
     * @return
     * @throws Exception
     */
    public static ImageBlock toBlock(ImgAll imgAll) throws Exception{
        return new ImageBlock(imgAll);
    }

    /**
     *
     * @param f  图像文件
     * @return
     * @throws Exception
     */
    public static Pair<ImgItem,ImgFile> parseImgFiles(File f) throws Exception{
        if (!ImageExtractor.isImage(f))
            return null;
        //获取该IMG文件的边界矩形
        Envelope envelope = ImageExtractor.parseToEnvelope(f);

        byte[] bs = cn.edu.cug.cs.gtl.io.File.readRawFile(f).array();
        Pair<String,byte[]> p = new Pair<>(f.getName(),bs);
        List<Pair<String,byte[]>> ls= new ArrayList<>();
        ls.add(p);


        //设置IMGItem
        ImgItem imgItem = new ImgItem();
        imgItem.setImgInternalId(BigDecimal.valueOf(-1));
        imgItem.setImgEnTitle(p.getFirst());
        imgItem.setImgCnTitle(p.getFirst());
        if(envelope.isNull()) {
            imgItem.setImgExtent(
                    new StringBuffer()
                            .append(String.valueOf(envelope.getMinX()))
                            .append(FileDataSplitter.COMMA.getDelimiter())
                            .append(String.valueOf(envelope.getMaxX()))
                            .append(FileDataSplitter.COMMA.getDelimiter())
                            .append(String.valueOf(envelope.getMinY()))
                            .append(FileDataSplitter.COMMA.getDelimiter())
                            .append(String.valueOf(envelope.getMaxY()))
                            .toString()
            );
        }
        imgItem.setImgCreateDate(new Date());
        imgItem.setImgAppendDate(new Date());
        imgItem.setImgUpdateDate(new Date());
        imgItem.setImgType("IMG");
        //设置IMGFile
        ImgFile imgFile = new ImgFile();
        imgFile.setImgInternalId(imgItem.getImgInternalId());
        byte[] zipBytes = ZipUtils.zip(ls);
        imgFile.setImgRawBytes(zipBytes);
        return new Pair<>(imgItem,imgFile);
    }

    /**
     * 获取分类目录树
     * @return
     */
    public CatalogTree getCatalogTree()  {
        try {
            List<ImgCatalog> imgCatalogs =getCatalogs();
            DatabaseMetaData metaData = session.getDataSource().getConnection().getMetaData();
            if(metaData!=null )
                return new CatalogTree(imgCatalogs,metaData.getURL());
            else
                return new CatalogTree(imgCatalogs);
        }
        catch (SQLException e){
            e.printStackTrace();
        }
        return null;
    }


    /**
     *
     * @param catalogId
     * @return
     */
    public List<BigDecimal> getImgInternalIdsByCatalogInternalId(BigDecimal catalogId){
        if(session.isClosed()){
            session = Session.getMasterSession();
        }
        ImgItemMapper imgItemMapper = session.getMapper(ImgItemMapper.class);
        return imgItemMapper.getImgInternalIdsByCatalogInternalId(catalogId);
    }


}
